package redsocialujaen_pa;


import excepciones.*;
import java.io.Serializable;
import java.security.*;
import java.util.*;
import javax.persistence.*;

/**
 *
 * @author Carmen M. Morillo Arias
 *         David Contreras García
 */

@Entity
@Table(name = "USUARIO")
public class Usuario implements Serializable{
    
    @Id
    private String email;
    
    @Column(name = "NOMBRE")
    private String nombre;
    
    @Column(name = "CLAVE")
    private String clave;
    
    @Column(name = "DESCRIPCION")
    private String descripcion;
    
    @ManyToMany(mappedBy = "amigos")
    private List<Usuario> amigos;
    
    @OneToMany
    private List<Usuario> solicitudesAmistad;
    
    @OneToMany
    private List<MensajePrivado> mensajesPrivados;
    
    @OneToMany
    private List<Mensaje> mensajesRecibidos;
    
    
    public String getNombre(){ return nombre;}
    public String getDescripcion(){ return descripcion;}
    public String getEmail(){ return email;}
    
    public List<Mensaje> getMensajesMuro(){ return  (List<Mensaje>) mensajesRecibidos;}
    public List<MensajePrivado> getMensajesPrivados(){ return (List<MensajePrivado>) mensajesPrivados;}
    public List<Usuario> getAmigos(){ return (List<Usuario>) amigos;}
    public List<Usuario> getSolicitudesAmistad(){ return (List<Usuario>) solicitudesAmistad;}
    
    
    /**
     * Método privado que comprueba si un usuario es amigo de otro
     * @return true si el usuario es amigo, false en caso contrario 
     */
    private boolean comprobarSiAmigo(Usuario usuario) {
        
        for (Usuario u: amigos) {
            if (u == usuario) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Comprueba que el usuario hizo una solicitud de amistad
     * @return true si solicitó ser amigo, false en otro caso
     */
    private boolean comprobarSiSolicitante(Usuario usuario){
    
        for (Usuario user: solicitudesAmistad){
            
            if (user == usuario){
                return true;
            }
        }
        return false;
    }
    
    /**
     * @see admitirAmigo 
     */
    private void agregarAmigo(Usuario u){
       u.amigos.add(this);
    }
    
    /**
     * Constructor
     */
    public Usuario(){
        amigos = new LinkedList<Usuario>();
        solicitudesAmistad = new LinkedList<Usuario>();
        mensajesRecibidos = new LinkedList<Mensaje>();
        mensajesPrivados = new LinkedList<MensajePrivado>();
    }
    
    /**
     * Constructor copia
     */
    public Usuario(String e,String n,String c,String d){
        email = e;
        nombre = n;
        clave = md5(c);
        descripcion = d;
        amigos = new LinkedList<Usuario>();
        solicitudesAmistad = new LinkedList<Usuario>();
        mensajesRecibidos = new LinkedList<Mensaje>();
        mensajesPrivados = new LinkedList<MensajePrivado>();
        
    }
    
    /**
     * Algoritmo MD5 
     * @see claveValida
     * @return cadena codificada
     */
    static String md5(String s){ 

        String r = null;    
            try {
                if (s != null) {
                    MessageDigest algorithm = MessageDigest.getInstance("MD5");
                    algorithm.reset();
                    algorithm.update(s.getBytes());
                    byte bytes[] = algorithm.digest();
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < bytes.length; i++) {
                        String hex = Integer.toHexString(0xff & bytes[i]);
                        if (hex.length() == 1)
                        sb.append('0');
                        sb.append(hex);
                        }
                    r = sb.toString();
                    }
                } catch (NoSuchAlgorithmException e) {}
        
        return r;
    }
   
    /**
     * Comprueba si la clave se válida
     * @see loginUsuario
     * @return true si la clave es válida, false en caso contrario 
     */
    boolean claveValida(String s){
        boolean valida = false;
    
        if (md5(s).compareTo(this.clave) == 0) valida = true;
        return valida;
    
    }       
    
    /**
     * Manda un mensaje al muro. El mensaje se envía también a todos sus amigos de la red
     * 
     */
    public void mensajeMuro(String msg){
        Mensaje m = new Mensaje(msg,new Date(),this);
        Iterator<Usuario> it = amigos.iterator();
        
        this.recibirMensaje(m);
        if(!amigos.isEmpty()){
            while(it.hasNext()){
                it.next().recibirMensaje(m);
            }
        }
    }
    
    /**
     * Envia un mensaje privado a un amigo
     * @see comprobarSiAmigo
     * @throws ExcepcionUsuarioNoAmigo: no puede enviar un mensaje a un usuario si no figura en su lista de amigos
     */
    public void mensajeAmigo(String msg, Usuario amigo) throws ExcepcionUsuarioNoAmigo {
        if (!comprobarSiAmigo(amigo)) {
            throw new ExcepcionUsuarioNoAmigo();
        }
        
        MensajePrivado m = new MensajePrivado(msg,new Date(),this,amigo);
        amigo.recibirMensaje(m);
    }
    
    /**
     * Añade un usuario a la lista de solicitudes de amistad 
     */
    public void solicitudAmistad(Usuario u){
        solicitudesAmistad.add(u);

    }
    
    /**
     * Añade a un amigo su lista de amigos. Este método es recíproco, para ambos usuarios
     * @see agregarAmigo
     * @see comprobarSiSolicitante
     * @throws ExcepcionUsuarioNoSolicitante: no puede agregase un amigo si no figura en la lista de solicitudes 
     */
    public void admitirAmigo(Usuario usuario) throws ExcepcionUsuarioNoSolicitante {

        if(!comprobarSiSolicitante(usuario)){
            throw new ExcepcionUsuarioNoSolicitante();
        }
        else{
            amigos.add(usuario);
            solicitudesAmistad.remove(usuario);
            agregarAmigo(usuario);     
        }
    }
    
    /**
     * Agrega un mensaje a la lista de mensajes correspondiente en función de si son privados o públicos
     */
    public void recibirMensaje(Mensaje m){
        MensajePrivado mp;
        
        if(m instanceof MensajePrivado){
            mp = (MensajePrivado) m;
            mensajesPrivados.add(mp);
    
        }
        else{
            mensajesRecibidos.add(m);
        }  
    }
    
}
